
package JET.physics.engine;

import JET.editor.SelectionGroup;
import JET.geom.Rect2d;
import JET.geom.Vec2d;
import JET.physics.*;
import JET.physics.bodies.Body;
import JET.physics.bodies.RigidBody;
import JET.physics.bodies.RigidBodyHelper;
import JET.physics.bodies.SpringBody;
import java.util.ArrayList;
import java.util.Collection;

/**
 *
 * @author raos
 */
public final class JETContextHelper {

    public static ArrayList<Mass> getSpringBodyMassInRect2dList(JETContext engine, Rect2d rect2d) {
        ArrayList<Mass> list = new ArrayList<Mass>();

        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol) {
                Collection<Mass> massCol = sb.getMassListSync();
                synchronized(massCol) {
                    for( Mass mass : massCol)
                        if( rect2d.contains(mass.getPosition()) )
                            list.add( mass );
                }
            }
        }

        return list;
    }

    public static Spring getSpringBodySpringAtPos(JETContext engine, Vec2d vec, double catchAreaScale) {
        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol) {
                Collection<Spring> springCol = sb.getSpringListSync();
                synchronized(springCol) {
                    for(Spring spring : springCol)
                        if( spring.getCatchArea(catchAreaScale).contains(vec) )
                            return spring;
                }
            }
        }
        return null;
    }
    public static RSpring getSpringBodyRSpringAtPos(JETContext engine, Vec2d vec, double catchAreaScale) {
        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol)
                for( RSpring rSpring : sb.getRSpringList())
                    if( rSpring.getCatchArea(1, catchAreaScale).contains(vec) ||
                        rSpring.getCatchArea(2, catchAreaScale).contains(vec) ||
                        rSpring.getCatchArea(3, catchAreaScale).contains(vec)  )
                        return rSpring;
        }
        return null;
    }

    public static Wall getSpringBodyWallAtPos(JETContext engine, Vec2d vec, double catchAreaScale) {
        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol)
                for( Wall wall : sb.getBorder().getWallList() )
                    if( wall.getCatchArea(catchAreaScale).contains(vec) )
                        return wall;
        }
        return null;
    }

    public static Mass getSpringBodyMassInRect2d(JETContext engine, Rect2d rect2d) {
        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol) {
                Collection<Mass> massCol = sb.getMassListSync();
                synchronized(massCol) {
                    for(Mass mass : massCol)
                        if( rect2d.contains(mass.getPosition()) )
                            return mass;
                }
            }
        }


        return null;
    }
    public static Mass getSpringBodyMassClosestToVec2d(JETContext engine, Vec2d v, boolean moveable) {
        double minDistSquared = Double.MAX_VALUE;
        Mass closestMass = null;

        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for( SpringBody sb : sbCol ) {
                Collection<Mass> massCol = sb.getMassListSync();
                synchronized(massCol) {
                    for(Mass mass : massCol)
                        if (moveable && mass.isMoveable()) {
                            double dist = v.getSubbed(mass.getPosition()).getMagnitudeSquared();
                            if( dist<=minDistSquared ) {
                                closestMass = mass;
                                minDistSquared = dist;
                            }
                        }
                }
            }
        }

        return closestMass;
    }
    public static ArrayList<Mass> getSpringBodyMassesClosestToVec2d(JETContext engine, Vec2d v, int count) {
        double[] minDistSquared = new double[count];
        for(int i=0; i<count; i++)
            minDistSquared[i] = Double.MAX_VALUE;

        Mass[] closestMass = new Mass[count];

        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for( SpringBody sb : sbCol) {
                Collection<Mass> massCol = sb.getMassListSync();
                synchronized(massCol) {
                    for(Mass mass : massCol) {
                        double dist = v.getSubbed(mass.getPosition()).getMagnitudeSquared();
                        for(int i=0; i<count; i++)
                            if( dist<=minDistSquared[i] ) {
                                for(int n=count-1; n>i; n--) {
                                    closestMass[n] = closestMass[n-1];
                                    minDistSquared[n] = minDistSquared[n-1];
                                }
                                closestMass[i] = mass;
                                minDistSquared[i] = dist;
                                break;
                            }
                    }
                }
            }
        }

        ArrayList retList = new ArrayList(count);
        for(int i=0; i<count; i++)
            if (closestMass[i]!=null)
                retList.add(closestMass[i]);

        return retList;
    }

    public static SpringBody getSpringBodyThatOwnMass(JETContext engine, Mass mass) {
        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol)
                if ( sb.isOwnerOf(mass) )
                    return sb;
        }
        return null;
    }
    public static SpringBody getSpringBodyThatOwnWall(JETContext engine, Wall wall) {
        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol)
                if ( sb.isOwnerOf(wall) )
                    return sb;
        }
        return null;
    }
    public static SpringBody getSpringBodyThatOwnSpring(JETContext engine, Spring spring) {
        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol)
                if ( sb.isOwnerOf(spring) )
                    return sb;
        }
        return null;
    }
    public static SpringBody getSpringBodyThatOwnRSpring(JETContext engine, RSpring rSpring) {
        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol)
                if ( sb.isOwnerOf(rSpring) )
                    return sb;
        }
        return null;
    }
    public static ArrayList<Spring> getSpringListOfMass(JETContext engine, Mass mass, boolean fromEngine, boolean fromSpringBody) {
        ArrayList<Spring> springs = new ArrayList<Spring>();

        if (fromSpringBody) {
            SpringBody sb = JETContextHelper.getSpringBodyThatOwnMass(engine, mass);
            if (sb!=null) {
                Collection<Spring> sbSpringCol = sb.getSpringListSync();
                synchronized(sbSpringCol) {
                    for(Spring spring : sbSpringCol)
                        if (spring.getM1().equals(mass) || spring.getM2().equals(mass))
                            springs.add(spring);
                }
            }
        }

        if (fromEngine) {
            Collection<Spring> engSpringCol = engine.getSpringListSync();
            synchronized(engSpringCol) {
                for(Spring spring : engSpringCol)
                    if (spring.getM1().equals(mass) || spring.getM2().equals(mass))
                        springs.add(spring);
            }
        }

        return springs;
    }
    
    public static ArrayList<RSpring> getRSpringListOfMass(JETContext engine, Mass mass, boolean fromEngine, boolean fromSpringBodies)
    {
        ArrayList<RSpring> rSprings = new ArrayList<RSpring>();

        if (fromSpringBodies) {
            SpringBody sb = JETContextHelper.getSpringBodyThatOwnMass(engine, mass);
            if (sb!=null) {
                Collection<RSpring> rSpringCol = sb.getRSpringListSync();
                synchronized(rSpringCol) {
                    for(RSpring rSpring : rSpringCol)
                        if (rSpring.getM1().equals(mass) || rSpring.getM2().equals(mass) || rSpring.getM3().equals(mass))
                            rSprings.add(rSpring);
                }
            }
        }

        if (fromEngine)
        {
            Collection<RSpring> engRSpringCol = engine.getRSpringListSync();
            synchronized(engRSpringCol) {
                for(RSpring rSpring : engRSpringCol)
                    if (rSpring.getM1().equals(mass) || rSpring.getM2().equals(mass) || rSpring.getM3().equals(mass))
                        rSprings.add(rSpring);
            }
        }

        return rSprings;
    }
    public static ArrayList<Wall> getWallListOfMass(JETContext engine, Mass mass)
    {
        ArrayList<Wall> walls = new ArrayList<Wall>();

        SpringBody sb = JETContextHelper.getSpringBodyThatOwnMass(engine, mass);
        if (sb!=null)
            for(Wall wall : sb.getBorder().getWallList())
                if (wall.getM1().equals(mass) || wall.getM2().equals(mass))
                    walls.add(wall);

        return walls;
    }

    public static SelectionGroup getItemsInRect(JETContext engine, Rect2d rect)
    {
        // TODO : lista ma zawierac wszyzstkie obiekty w graphic panelu aktualnie wskazywane przez mPos...

        SelectionGroup group = new SelectionGroup(engine.scene);

        group.addMasses( JETContextHelper.getMassesInRect(engine, rect) );
        group.addSprings( JETContextHelper.getSpringsInRect(engine, rect) );
        group.addWalls( JETContextHelper.getWallsInRect(engine, rect) );

        return group;
    }

    public static Object getItemClosestToVec2d(JETContext ctx, Vec2d v)
    {
        Object found = null;
        double minDist = Double.MAX_VALUE;
        
        Mass m = getSpringBodyMassClosestToVec2d(ctx, v, true);
        if (m!=null) {
            minDist = m.getPosition().getSubbed(v).getMagnitudeSquared();
            found = m;
        }

        RigidBody rb = getRigidBodyClosestToVec(ctx, v);
        if (rb!=null) {
            int index = RigidBodyHelper.getRigidBodyVertexIndexClosestToVec(rb, v);
            double dist = rb.dynVecList.get(index).getPosition().getSubbed(v).getMagnitudeSquared();

            if (dist<minDist) {
                found = rb;
                minDist = dist;
            }
        }

        // TODO: maybe another kind of items ....
        // ...
        
        return found;
    }

    /**
     *
     * @param mPos position at wchich we look for spring body correpsonding to engine system
     * @return spring body at mPos position
     */
    public static SpringBody getSpringBodyAt(JETContext engine, Vec2d mPos) {
        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol)
                if ( sb.getBorder().contains(mPos) )
                    return sb;
        }

        return null;
    }

    public static ArrayList<Mass> getMassesInRect(JETContext engine, Rect2d rect)
    {
        ArrayList<Mass> returnMassList = new ArrayList<Mass>();

        Collection<SpringBody> sbCol = engine.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol) {
                Collection<Mass> massCol = sb.getMassListSync();
                synchronized(massCol) {
                    for(Mass mass : massCol)
                        if (rect.contains(mass.getPosition()))
                            returnMassList.add(mass);
                }
            }
        }

        return returnMassList;
    }

    public static ArrayList<Spring> getSpringsInRect(JETContext engine, Rect2d rect)
    {
        ArrayList<Spring> returnSpringList = new ArrayList<Spring>();

        // Sprezyny z cial sprezystych
        for(SpringBody sb : engine.springBodyList)
            for(Spring spring : sb.getSpringList())
                if ( rect.contains(spring.getM1().getPosition()) ||
                     rect.contains(spring.getM2().getPosition()) ||
                     rect.contains(spring.getCenter())            )
                    returnSpringList.add(spring);

        // Sprezyny z silnika
        for(Spring spring : engine.springList)
            if ( rect.contains(spring.getM1().getPosition()) ||
                 rect.contains(spring.getM2().getPosition()) ||
                 rect.contains(spring.getCenter())            )
                returnSpringList.add(spring);

        return returnSpringList;
    }

    public static ArrayList<RSpring> getRSpringsInRect(JETContext engine, Rect2d rect)
    {
        ArrayList<RSpring> returnRSpringList = new ArrayList<RSpring>();

        // Sprezyny z cial sprezystych
        for(SpringBody sb : engine.springBodyList)
            for(RSpring rs : sb.getRSpringList())
                if ( rect.contains(rs.getM1().getPosition()) ||
                     rect.contains(rs.getM2().getPosition()) ||
                     rect.contains(rs.getM3().getPosition()) ||
                     rect.contains(rs.getA().getMidVec(.5).getAdded(rs.getM2().getPosition()))  ||
                     rect.contains(rs.getB().getMidVec(.5).getAdded(rs.getM2().getPosition()))   )
                    returnRSpringList.add(rs);

        // Sprezyny z silnika
        for(RSpring rs : engine.rSpringList)
            if ( rect.contains(rs.getM1().getPosition()) ||
                 rect.contains(rs.getM2().getPosition()) ||
                 rect.contains(rs.getM3().getPosition()) ||
                 rect.contains(rs.getA().getMidVec(.5).getAdded(rs.getM2().getPosition()))  ||
                 rect.contains(rs.getB().getMidVec(.5).getAdded(rs.getM2().getPosition()))   )
                returnRSpringList.add(rs);

        return returnRSpringList;
    }

    public static ArrayList<Wall> getWallsInRect(JETContext engine, Rect2d rect)
    {
        ArrayList<Wall> returnWallList = new ArrayList<Wall>();

        for(SpringBody sb : engine.springBodyList)
            for(Wall wall : sb.getBorder().getWallList())
                if ( rect.contains(wall.getM1().getPosition()) ||
                     rect.contains(wall.getM2().getPosition()) ||
                     rect.contains(wall.getCenter())            )
                    returnWallList.add(wall);

        return returnWallList;
    }

    public static RigidBody getRigidBodyAt(JETContext context, Vec2d pos) {
        Collection<RigidBody> rbCol = context.getRigidBodyListSync();
        synchronized(rbCol) {
            for(RigidBody rb : rbCol)
                if ( rb.border.contains(pos) )
                    return rb;
        }

        return null;
    }

    public static RigidBody getRigidBodyClosestToVec(JETContext ctx, Vec2d v) {
        double minDistSquared = Double.MAX_VALUE;
        int index = -1;
        RigidBody found = null;

        Collection<RigidBody> rbCol = ctx.getRigidBodyListSync();
        synchronized(rbCol) {
            for(RigidBody rb : rbCol)
                if ( (index=RigidBodyHelper.getRigidBodyVertexIndexClosestToVec(rb, v)) != -1) {
                    double dist = rb.dynVecList.get(index).getPosition().getSubbed(v).getMagnitudeSquared();
                    if (dist<minDistSquared) {
                        found = rb;
                        minDistSquared = dist;
                    }
                }
        }

        return found;
    }

    public static RigidBody getRigidBodyThatOwnMass(JETContext ctx, Mass mass) {
        Collection<RigidBody> rbCol = ctx.getRigidBodyListSync();
        synchronized(rbCol) {
            for(RigidBody rb : rbCol) {
                // Dynamic shape masses
                for(Mass m : rb.dynVecList)
                    if (mass.equals(m)) return rb;
                // Extra masses
                for(Mass m : rb.extraDynMassList)
                    if (mass.equals(m)) return rb;
            }
        }

        return null;
    }

    public static Body getBodyThatOwnsMass(JETContext ctx, Mass mass) {
        SpringBody sb = getSpringBodyThatOwnMass(ctx, mass);
        if (sb!=null) return sb;
        RigidBody rb = getRigidBodyThatOwnMass(ctx, mass);
        if (rb!=null) return rb;

        return null;
    }
}
